"""属性随机值生成工具。"""

from __future__ import annotations

import random
import re
import time
from typing import Dict, List, Sequence

from src.core.models import Device, DeviceAttribute
from src.services.bool_policy import BoolScheme, coerce_bool_value, detect_bool_scheme


class AttributeValueGenerator:
    """根据属性类型生成合适的随机测试值。"""

    _BOOL_TYPES = {"bool", "boolean"}
    _SIGNED_INT_BOUNDS: Dict[str, tuple[int, int]] = {
        "int": (0, 9),
        "int16": (0, 9),
        "int32": (0, 9),
        "int64": (0, 9),
    }
    _UNSIGNED_INT_MAX: Dict[str, int] = {
        "uint16": 9,
        "uint32": 9,
        "uint64": 9,
    }
    _FLOAT_TYPES = {"float", "double", "float32", "float64"}
    _STRING_TYPES = {"string", "text"}

    def generate_payload(self, device: Device, attributes: List[DeviceAttribute]) -> Dict[str, Dict[str, object]]:
        payload: Dict[str, Dict[str, object]] = {}

        for attr in attributes:
            payload[attr.resource_name] = self.generate_entry(attr)

        return payload

    def generate_entry(
        self,
        attr: DeviceAttribute,
        *,
        timestamp: int | None = None,
    ) -> Dict[str, object]:
        """为单个属性生成测试值。"""
        return {
            "value": self._generate_value(attr),
        }

    def generate_value(self, attr: DeviceAttribute):
        """仅返回单个属性的随机值，供定制化场景使用。"""
        return self._generate_value(attr)

    def _generate_value(self, attr: DeviceAttribute):
        value_type = (attr.value_type or "").strip().lower()

        if value_type in self._BOOL_TYPES:
            return self._generate_bool(attr)
        if value_type in self._SIGNED_INT_BOUNDS:
            # 优先检查units中是否包含数字
            units_numbers = self._extract_numbers_from_units(attr.units)
            if units_numbers:
                return random.choice(units_numbers)
            low, high = self._SIGNED_INT_BOUNDS[value_type]
            return random.randint(low, high)
        if value_type in self._UNSIGNED_INT_MAX:
            # 优先检查units中是否包含数字
            units_numbers = self._extract_numbers_from_units(attr.units)
            if units_numbers:
                return random.choice(units_numbers)
            return random.randint(0, self._UNSIGNED_INT_MAX[value_type])
        if value_type.startswith("uint") and value_type not in self._UNSIGNED_INT_MAX:
            # 优先检查units中是否包含数字
            units_numbers = self._extract_numbers_from_units(attr.units)
            if units_numbers:
                return random.choice(units_numbers)
            # 兜底处理未显式列出的无符号整数
            return random.randint(0, 9)
        if value_type.startswith("int"):
            # 处理其他int类型，优先检查units中是否包含数字
            units_numbers = self._extract_numbers_from_units(attr.units)
            if units_numbers:
                return random.choice(units_numbers)
            # 默认返回0-9的随机数
            return random.randint(0, 9)
        if value_type in self._FLOAT_TYPES:
            return round(random.uniform(0, 9), 2)
        if value_type in self._STRING_TYPES:
            return self._random_string(attr)

        return self._fallback_value(attr)

    @staticmethod
    def _current_timestamp() -> int:
        return int(time.time() * 1000)

    def timestamp(self) -> int:
        """对外暴露当前时间戳，便于生成 MQTT 消息。"""
        return self._current_timestamp()

    def _fallback_value(self, attr: DeviceAttribute):
        enum_candidates = self._enum_candidates(attr)
        if enum_candidates:
            return random.choice(enum_candidates)

        default = self._coerce_literal(attr, attr.default_value)
        if default is not None:
            return default

        return self._random_string(attr)

    def _enum_candidates(self, attr: DeviceAttribute) -> Sequence[object]:
        value_type = (attr.value_type or "").strip().lower()
        if not attr.optional_values:
            return []

        candidates: List[object] = []
        for raw_value in attr.optional_values.keys():
            coerced = self._coerce_literal(attr, raw_value)
            if coerced is not None:
                candidates.append(coerced)
        return candidates

    def _generate_bool(self, attr: DeviceAttribute):
        scheme = detect_bool_scheme(attr.units)
        if scheme == "numeric":
            return random.choice([0, 1])
        return random.choice([True, False])

    def _coerce_literal(self, attr: DeviceAttribute, raw: str | None):
        value_type = (attr.value_type or "").strip().lower()
        if raw is None:
            return None

        text = str(raw).strip()
        if not text:
            return None

        if value_type in self._BOOL_TYPES:
            scheme: BoolScheme = detect_bool_scheme(attr.units)
            try:
                return coerce_bool_value(text, scheme)
            except ValueError:
                return None

        if value_type in self._SIGNED_INT_BOUNDS or value_type.startswith("int"):
            try:
                return int(text)
            except ValueError:
                return None

        if value_type in self._UNSIGNED_INT_MAX or value_type.startswith("uint"):
            try:
                return int(text)
            except ValueError:
                return None

        if value_type in self._FLOAT_TYPES:
            try:
                return float(text)
            except ValueError:
                return None

        return text

    def _random_string(self, attr: DeviceAttribute) -> str:
        """生成兼容数据合规要求的占位字符串。"""
        base = attr.resource_name or attr.nickname or "sample"
        sanitized = "".join(ch.lower() if ch.isalnum() else "_" for ch in base)
        sanitized = sanitized.strip("_") or "sample"
        suffix = random.randint(1000, 9999)
        return f"{sanitized}_{suffix}"
    
    def _extract_numbers_from_units(self, units: str | None) -> List[int]:
        """从units字段中提取所有数字，返回数字列表。
        
        例如：
        - "0,1" -> [0, 1]
        - "0,1,2" -> [0, 1, 2]
        - "0 1 2" -> [0, 1, 2]
        - "0-2" -> [0, 1, 2] (如果包含范围，则展开)
        - "状态0,状态1" -> [0, 1]
        - "10,11,12" -> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (如果数字大于9，则用0-9代替)
        
        Returns:
            提取的数字列表，如果没有找到数字则返回空列表。
            如果提取的数字中有大于9的，则返回[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]。
        """
        if not units:
            return []
        
        # 使用正则表达式提取所有数字
        numbers = re.findall(r'\d+', str(units))
        if not numbers:
            return []
        
        # 转换为整数并去重，保持顺序
        result = []
        seen = set()
        for num_str in numbers:
            try:
                num = int(num_str)
                if num not in seen:
                    result.append(num)
                    seen.add(num)
            except ValueError:
                continue
        
        # 如果提取的数字中有大于9的，就用0-9的列表代替
        if result and any(num > 9 for num in result):
            return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
        
        return result


